// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#pragma once
#include <nebula/version.h>
#ifdef NEBULA_HDFS
#include <cstddef>
#include <cstdint>

#include <hdfs.h>
#include <turbo/utility/status.h>
#include <turbo/base/macros.h>
#include <nebula/util/windows_compatibility.h>  // IWYU pragma: keep

using std::size_t;

struct hdfsBuilder;

namespace nebula {

    namespace io::internal {

        // NOTE(wesm): cpplint does not like use of short and other imprecise C types
        struct LibHdfsShim {
            void *handle;

            hdfsBuilder *(*hdfsNewBuilder)(void);

            void (*hdfsBuilderSetNameNode)(hdfsBuilder *bld, const char *nn);

            void (*hdfsBuilderSetNameNodePort)(hdfsBuilder *bld, tPort port);

            void (*hdfsBuilderSetUserName)(hdfsBuilder *bld, const char *userName);

            void (*hdfsBuilderSetKerbTicketCachePath)(hdfsBuilder *bld,
                                                      const char *kerbTicketCachePath);

            void (*hdfsBuilderSetForceNewInstance)(hdfsBuilder *bld);

            hdfsFS (*hdfsBuilderConnect)(hdfsBuilder *bld);

            int (*hdfsBuilderConfSetStr)(hdfsBuilder *bld, const char *key, const char *val);

            int (*hdfsDisconnect)(hdfsFS fs);

            hdfsFile (*hdfsOpenFile)(hdfsFS fs, const char *path, int flags, int bufferSize,
                                     short replication, tSize blocksize);  // NOLINT

            int (*hdfsCloseFile)(hdfsFS fs, hdfsFile file);

            int (*hdfsExists)(hdfsFS fs, const char *path);

            int (*hdfsSeek)(hdfsFS fs, hdfsFile file, tOffset desiredPos);

            tOffset (*hdfsTell)(hdfsFS fs, hdfsFile file);

            tSize (*hdfsRead)(hdfsFS fs, hdfsFile file, void *buffer, tSize length);

            tSize (*hdfsPread)(hdfsFS fs, hdfsFile file, tOffset position, void *buffer,
                               tSize length);

            tSize (*hdfsWrite)(hdfsFS fs, hdfsFile file, const void *buffer, tSize length);

            int (*hdfsFlush)(hdfsFS fs, hdfsFile file);

            int (*hdfsAvailable)(hdfsFS fs, hdfsFile file);

            int (*hdfsCopy)(hdfsFS srcFS, const char *src, hdfsFS dstFS, const char *dst);

            int (*hdfsMove)(hdfsFS srcFS, const char *src, hdfsFS dstFS, const char *dst);

            int (*hdfsDelete)(hdfsFS fs, const char *path, int recursive);

            int (*hdfsRename)(hdfsFS fs, const char *oldPath, const char *newPath);

            char *(*hdfsGetWorkingDirectory)(hdfsFS fs, char *buffer, size_t bufferSize);

            int (*hdfsSetWorkingDirectory)(hdfsFS fs, const char *path);

            int (*hdfsCreateDirectory)(hdfsFS fs, const char *path);

            int (*hdfsSetReplication)(hdfsFS fs, const char *path, int16_t replication);

            hdfsFileInfo *(*hdfsListDirectory)(hdfsFS fs, const char *path, int *numEntries);

            hdfsFileInfo *(*hdfsGetPathInfo)(hdfsFS fs, const char *path);

            void (*hdfsFreeFileInfo)(hdfsFileInfo *hdfsFileInfo, int numEntries);

            char ***(*hdfsGetHosts)(hdfsFS fs, const char *path, tOffset start, tOffset length);

            void (*hdfsFreeHosts)(char ***blockHosts);

            tOffset (*hdfsGetDefaultBlockSize)(hdfsFS fs);

            tOffset (*hdfsGetCapacity)(hdfsFS fs);

            tOffset (*hdfsGetUsed)(hdfsFS fs);

            int (*hdfsChown)(hdfsFS fs, const char *path, const char *owner, const char *group);

            int (*hdfsChmod)(hdfsFS fs, const char *path, short mode);  // NOLINT
            int (*hdfsUtime)(hdfsFS fs, const char *path, tTime mtime, tTime atime);

            void Initialize() {
                this->handle = nullptr;
                this->hdfsNewBuilder = nullptr;
                this->hdfsBuilderSetNameNode = nullptr;
                this->hdfsBuilderSetNameNodePort = nullptr;
                this->hdfsBuilderSetUserName = nullptr;
                this->hdfsBuilderSetKerbTicketCachePath = nullptr;
                this->hdfsBuilderSetForceNewInstance = nullptr;
                this->hdfsBuilderConfSetStr = nullptr;
                this->hdfsBuilderConnect = nullptr;
                this->hdfsDisconnect = nullptr;
                this->hdfsOpenFile = nullptr;
                this->hdfsCloseFile = nullptr;
                this->hdfsExists = nullptr;
                this->hdfsSeek = nullptr;
                this->hdfsTell = nullptr;
                this->hdfsRead = nullptr;
                this->hdfsPread = nullptr;
                this->hdfsWrite = nullptr;
                this->hdfsFlush = nullptr;
                this->hdfsAvailable = nullptr;
                this->hdfsCopy = nullptr;
                this->hdfsMove = nullptr;
                this->hdfsDelete = nullptr;
                this->hdfsRename = nullptr;
                this->hdfsGetWorkingDirectory = nullptr;
                this->hdfsSetWorkingDirectory = nullptr;
                this->hdfsCreateDirectory = nullptr;
                this->hdfsSetReplication = nullptr;
                this->hdfsListDirectory = nullptr;
                this->hdfsGetPathInfo = nullptr;
                this->hdfsFreeFileInfo = nullptr;
                this->hdfsGetHosts = nullptr;
                this->hdfsFreeHosts = nullptr;
                this->hdfsGetDefaultBlockSize = nullptr;
                this->hdfsGetCapacity = nullptr;
                this->hdfsGetUsed = nullptr;
                this->hdfsChown = nullptr;
                this->hdfsChmod = nullptr;
                this->hdfsUtime = nullptr;
            }

            hdfsBuilder *NewBuilder(void);

            void BuilderSetNameNode(hdfsBuilder *bld, const char *nn);

            void BuilderSetNameNodePort(hdfsBuilder *bld, tPort port);

            void BuilderSetUserName(hdfsBuilder *bld, const char *userName);

            void BuilderSetKerbTicketCachePath(hdfsBuilder *bld, const char *kerbTicketCachePath);

            void BuilderSetForceNewInstance(hdfsBuilder *bld);

            int BuilderConfSetStr(hdfsBuilder *bld, const char *key, const char *val);

            hdfsFS BuilderConnect(hdfsBuilder *bld);

            int Disconnect(hdfsFS fs);

            hdfsFile OpenFile(hdfsFS fs, const char *path, int flags, int bufferSize,
                              short replication, tSize blocksize);  // NOLINT

            int CloseFile(hdfsFS fs, hdfsFile file);

            int Exists(hdfsFS fs, const char *path);

            int Seek(hdfsFS fs, hdfsFile file, tOffset desiredPos);

            tOffset tell(hdfsFS fs, hdfsFile file);

            tSize read(hdfsFS fs, hdfsFile file, void *buffer, tSize length);

            bool HasPread();

            tSize Pread(hdfsFS fs, hdfsFile file, tOffset position, void *buffer, tSize length);

            tSize write(hdfsFS fs, hdfsFile file, const void *buffer, tSize length);

            int flush(hdfsFS fs, hdfsFile file);

            int Available(hdfsFS fs, hdfsFile file);

            int copy(hdfsFS srcFS, const char *src, hdfsFS dstFS, const char *dst);

            int Move(hdfsFS srcFS, const char *src, hdfsFS dstFS, const char *dst);

            int Delete(hdfsFS fs, const char *path, int recursive);

            int Rename(hdfsFS fs, const char *oldPath, const char *newPath);

            char *GetWorkingDirectory(hdfsFS fs, char *buffer, size_t bufferSize);

            int SetWorkingDirectory(hdfsFS fs, const char *path);

            int MakeDirectory(hdfsFS fs, const char *path);

            int SetReplication(hdfsFS fs, const char *path, int16_t replication);

            hdfsFileInfo *ListDirectory(hdfsFS fs, const char *path, int *numEntries);

            hdfsFileInfo *GetPathInfo(hdfsFS fs, const char *path);

            void FreeFileInfo(hdfsFileInfo *hdfsFileInfo, int numEntries);

            char ***GetHosts(hdfsFS fs, const char *path, tOffset start, tOffset length);

            void FreeHosts(char ***blockHosts);

            tOffset GetDefaultBlockSize(hdfsFS fs);

            tOffset get_capacity(hdfsFS fs);

            tOffset GetUsed(hdfsFS fs);

            int Chown(hdfsFS fs, const char *path, const char *owner, const char *group);

            int Chmod(hdfsFS fs, const char *path, short mode);  // NOLINT

            int Utime(hdfsFS fs, const char *path, tTime mtime, tTime atime);

            turbo::Status GetRequiredSymbols();
        };

        // TODO(wesm): Remove these exports when we are linking statically
        TURBO_EXPORT turbo::Status ConnectLibHdfs(LibHdfsShim **driver);

    }  // namespace io::internal
}  // namespace nebula
#endif